﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Diagnostics;

using SharedGenomics.Core;
using SharedGenomics.Domain;

namespace SharedGenomics.DataAccess
{
    public class BeadStudioGenotypeFileParser : IGenotypeDataFileParser, IDisposable
    {
        public BeadStudioGenotypeFileParser(string filePath)
            : this(new BeadStudioGenotypeFileReader(filePath), null)
        {
        }

        public BeadStudioGenotypeFileParser(IBeadStudioGenotypeFileReader reader)
            : this(reader, null)
        {
        }

        public BeadStudioGenotypeFileParser(string filePath, IndividualSexLookup sexLookup)
            : this(new BeadStudioGenotypeFileReader(filePath), sexLookup)
        {
        }

        public BeadStudioGenotypeFileParser(IBeadStudioGenotypeFileReader fileReader, IndividualSexLookup sexLookup)
        {
            fileReader.ThrowIfNull("fileReader");

            this.Reader = fileReader;
            this.SexLookup = sexLookup;

            this._validationMessages = new ValidationMessageCollection();
            this.Validate();
        }

        public IValidationMessageCollection ValidationMessages
        {
            get { return this._validationMessages; }
        }

        public ReadOnlyCollection<string> IndividualNames
        {
            get { return new ReadOnlyCollection<string>(this._individualNames); }
        }

        public ReadOnlyCollection<string> MarkerNames
        {
            get { return new ReadOnlyCollection<string>(this.Reader.MarkerNames.ToList()); }
        }

        #region IEnumerable<IndividualGenotype> Members

        public IEnumerator<IndividualGenotype> GetEnumerator()
        {
            string[] snpNames = this.Reader.MarkerNames.ToArray();

            IndividualGenotype currentGenotype;
            for (int i = 0; i < this._individualNames.Count; i++)
            {
                string currentId = _individualNames[i];
                Sex currentSex = this.SexLookup == null ? Sex.Unknown : this.SexLookup[currentId];
                currentGenotype = new IndividualGenotype(currentId, currentSex);

                int snpIndex = 0;
                foreach (string sample in this.Reader.GetIndividualSamples(i))
                {
                    currentGenotype[snpNames[snpIndex]] = this.ParseSampling(sample);
                    snpIndex++;
                }

                yield return currentGenotype;
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion

        private void Validate()
        {
            this._individualNames = new List<string>();
            Dictionary<string, int> individualColumnIndices = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);
            var parsedHeaderItems = this.Reader.HeaderItems.Select(hi => this.ParseIndividualName(hi)).ToList();

            for(int i = 0; i < parsedHeaderItems.Count; i++)
            {
                string item = parsedHeaderItems[i];
                if (individualColumnIndices.ContainsKey(item))
                {
                    //duplicate column header - add an error indicating which columns are duplicates
                    int previousHeaderColumnIndex = individualColumnIndices[item];
                    InvalidDuplicateGenotypeDataValidationMessage message = new InvalidDuplicateGenotypeDataValidationMessage(item, previousHeaderColumnIndex, i, ValidationMessageSeverity.Error);
                    this._validationMessages.AddMessage(message);
                }
                else
                {
                    individualColumnIndices.Add(item, i);
                    this._individualNames.Add(item);
                }
            }
        }

        private string ParseIndividualName(string header)
        {
            Debug.Assert(header != null);
            return header.EndsWith(".GType")
                ? header.Substring(0, header.LastIndexOf(".GType"))
                : header;
        }

        private MarkerGenotype ParseSampling(string sampling)
        {
            switch (sampling)
            {
                case "AA": return MarkerGenotype.HomozygousMinor;
                case "BA": return MarkerGenotype.HeterozygousMajorMinor;
                case "AB": return MarkerGenotype.HeterozygousMinorMajor;
                case "BB": return MarkerGenotype.HomozygousMajor;
                default: return MarkerGenotype.Missing;
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            this.Reader.Dispose();
        }

        #endregion

        public IndividualSexLookup SexLookup { get; set; }
        private IBeadStudioGenotypeFileReader Reader { get; set; }
        private ValidationMessageCollection _validationMessages;
        private IList<string> _individualNames;
    }
}
